#pragma once

#include "Source.h"

/*Parameter class.								*/
/*This class is used to link between controller */
/*as a parameter can be plugged to a controller.*/
template <typename T> class Parameter : public Base
{
private:
	T					_currentValue; //the current value of the parameter
	Parameter<T>*		_source; //the source parameter (output of a controller)
	bool				_dirty; //if true, need to recalculate
	BaseController*		_parent; //the controller that owns this parameter
	bool				_output; //indicate if the parameter is an output parameter or a member of a controller

public:

	//Constructor
	Parameter();

	//Constructor for parameters
	Parameter(Source<T>* parent, T value, bool output = false);

	//Constructor for output parameter
	Parameter(Source<T>* parent);

	//Destructor
	~Parameter();

	//initialisation function
	void init(BaseController* parent, Parameter<T>* source, bool output = false);

	//Setter
	void setValue(const T& value);

	//Assignement operator
	const T&  operator= ( const T& );

	//Cast operator
	operator const T& ();

	//Connect a parameter to an other parameter
	void connectFrom(Parameter<T>* source);

	//Connect a parameter to a controller
	void connectFrom(Source<T>* source);

	//Dirty the parameter
	void onChange();

	//calculate and return the parameter value
	const T& getValue();
};

template <typename T> Parameter<T>::Parameter()
:_parent(0), _dirty(true), _output(false), _source(0)
{}

template <typename T> Parameter<T>::Parameter(Source<T>* parent, T value, bool output = false)
:_parent(parent), _currentValue(value), _dirty(true), _output(output), _source(0)
{}

template <typename T> Parameter<T>::Parameter(Source<T>* parent)
:_parent(parent), _dirty(true), _output(true), _source(0)
{}

template <typename T> Parameter<T>::~Parameter(){}

template <typename T> void Parameter<T>::init(BaseController* parent, Parameter<T>* source, bool output = false)
{
	_parent = parent;
	_output = output;
	_source = source;
}

template <typename T> void Parameter<T>::setValue(const T& value)
{
	_currentValue = value;
}

template <typename T> const T& Parameter<T>::getValue()
{
	if (_dirty)
    {
        if ( _output && _parent != 0 )
            _parent->onDemand();
        else if ( _source != 0 )
            _currentValue = _source->getValue();
        _dirty = false;
    }
    return _currentValue;

}

template <typename T> const T&  Parameter<T>::operator= ( const T& value)
{
	setValue(value);
	return getValue();
}

template <typename T> Parameter<T>::operator const T& ()
{
	return getValue();
}

template <typename T> void Parameter<T>::connectFrom(Parameter<T>* source)
{
	_source = source;
}

template <typename T> void Parameter<T>::connectFrom(Source<T>* source)
{
	_source = source->getOutputParameter();
}

template <typename T> void Parameter<T>::onChange()
{
	if (_dirty)
        return;

    _dirty = true;

    //if ( !_output && _parent != 0 )
	if(_parent != 0)
        _parent->onChange();
	if ( _source != 0)
		_source->onChange();

}